package com.paranoidmonkey.ode;

import java.util.ArrayList;
import java.util.List;

import com.paranoidmonkey.ode.Joint.Joint;

public class Body extends ODEObject{
	public Body(World world){
		handle=AndrODE.dBodyCreate(world.handle);
	}

	public Body(long handle){
		this.handle=handle;
	}
	
	public float getAutoDisableLinearThreshold (){
		return AndrODE.dBodyGetAutoDisableLinearThreshold(handle);
	}
	public void  setAutoDisableLinearThreshold (float linear_average_threshold){
		AndrODE.dBodySetAutoDisableLinearThreshold (handle, linear_average_threshold);
	}
	public float getAutoDisableAngularThreshold (){
		return AndrODE.dBodyGetAutoDisableAngularThreshold (handle);
	}
	public void  setAutoDisableAngularThreshold (float angular_average_threshold){
		AndrODE.dBodySetAutoDisableAngularThreshold (handle, angular_average_threshold);
	}
	public int getAutoDisableAverageSamplesCount (){
		return AndrODE.dBodyGetAutoDisableAverageSamplesCount (handle);
	}
	public void setAutoDisableAverageSamplesCount (int average_samples_count){
		AndrODE.dBodySetAutoDisableAverageSamplesCount (handle, average_samples_count);
	}
	public int getAutoDisableSteps (){
		return AndrODE.dBodyGetAutoDisableSteps (handle);
	}
	public void setAutoDisableSteps (int steps){
		AndrODE.dBodySetAutoDisableSteps (handle, steps);
	}
	public float getAutoDisableTime (){
		return AndrODE.dBodyGetAutoDisableTime (handle);
	}
	public void  setAutoDisableTime (float time){
		AndrODE.dBodySetAutoDisableTime (handle, time);
	}
	public int getAutoDisableFlag (){
		return AndrODE.dBodyGetAutoDisableFlag (handle);
	}
	public void setAutoDisableFlag (int do_auto_disable){
		AndrODE.dBodySetAutoDisableFlag (handle, do_auto_disable);
	}
	public void setAutoDisableDefaults (){
		AndrODE.dBodySetAutoDisableDefaults (handle);
	}
	public World getWorld (){
		return new World(AndrODE.dBodyGetWorld(handle));
	}
	public void destroy (){
		AndrODE.dBodyDestroy (handle);
	}
	public void setData (Object data){
		AndrODE.dBodySetData (handle, data);
	}
	public Object getData (){
		return AndrODE.dBodyGetData (handle);
	}
	public void setPosition   (float x, float y, float z){
		AndrODE.dBodySetPosition   (handle, x, y, z);
	}
	public void setPosition   (Vector3 pos){
		AndrODE.dBodySetPosition   (handle, pos.x(), pos.y(), pos.z());
	}
	public void setRotation   (Matrix3 R){
		AndrODE.dBodySetRotation   (handle, R.data);
	}
	public void setQuaternion (Quaternion q){
		AndrODE.dBodySetQuaternion (handle,  q.data);
	}
	public void setLinearVel  (float x, float y, float z){
		AndrODE.dBodySetLinearVel  (handle, x, y, z);
	}
	public void setAngularVel (float x, float y, float z){
		AndrODE.dBodySetAngularVel (handle, x, y, z);
	}
	public void setLinearVel  (Vector3 vel){
		AndrODE.dBodySetLinearVel  (handle, vel.x(), vel.y(), vel.z());
	}
	public void setAngularVel (Vector3 vel){
		AndrODE.dBodySetAngularVel (handle, vel.x(), vel.y(), vel.z());
	}
	public Vector3 getPosition (){
		return new Vector3(AndrODE.dBodyGetPosition (handle));
	}
	public void copyPosition (Vector3 pos){
		AndrODE.dBodyCopyPosition (handle,  pos.data);
	}
	public Matrix3 getRotation (){
		return new Matrix3(AndrODE.dBodyGetRotation (handle));
	}
	public void copyRotation (Matrix3 R){
		AndrODE.dBodyCopyRotation (handle, R.data);
	}
	public Quaternion getQuaternion (){
		return new Quaternion(AndrODE.dBodyGetQuaternion (handle));
	}
	public void copyQuaternion(Quaternion quat){
		AndrODE.dBodyCopyQuaternion(handle, quat.data);
	}
	public  Vector3 getLinearVel(){
		return new Vector3(AndrODE.dBodyGetLinearVel (handle));
	}
	public  Vector3 getAngularVel(){
		return new Vector3(AndrODE.dBodyGetAngularVel (handle));
	}
	public void setMass (Mass mass){
		AndrODE.dBodySetMass (handle,  mass.handle);
	}
	public void getMass (Mass mass){
		AndrODE.dBodyGetMass (handle, mass.handle);
	}
	public void addForce(float fx, float fy, float fz){
		AndrODE.dBodyAddForce(handle, fx, fy, fz);
	}
	public void addTorque(float fx, float fy, float fz){
		AndrODE.dBodyAddTorque(handle, fx, fy, fz);
	}
	public void addRelForce(float fx, float fy, float fz){
		AndrODE.dBodyAddRelForce(handle, fx, fy, fz);
	}
	public void addRelTorque(float fx, float fy, float fz){
		AndrODE.dBodyAddRelTorque(handle, fx, fy, fz);
	}
	public void addForceAtPos(float fx, float fy, float fz, float px, float py, float pz){
		AndrODE.dBodyAddForceAtPos(handle, fx, fy, fz, px, py, pz);
	}
	public void addForceAtRelPos(float fx, float fy, float fz, float px, float py, float pz){
		AndrODE.dBodyAddForceAtRelPos(handle, fx, fy, fz, px, py, pz);
	}
	public void addRelForceAtPos(float fx, float fy, float fz, float px, float py, float pz){
		AndrODE.dBodyAddRelForceAtPos(handle, fx, fy, fz, px, py, pz);
	}
	public void addRelForceAtRelPos(float fx, float fy, float fz, float px, float py, float pz){
		AndrODE.dBodyAddRelForceAtRelPos(handle, fx, fy, fz, px, py, pz);
	}
	public Vector3 getForce (){
		return new Vector3(AndrODE.dBodyGetForce (handle));
	}
	public Vector3 getTorque (){
		return new Vector3(AndrODE.dBodyGetTorque (handle));
	}
	public void setForce  (float x, float y, float z){
		AndrODE.dBodySetForce  (handle, x, y, z);
	}
	public void setTorque (float x, float y, float z){
		AndrODE.dBodySetTorque (handle, x, y, z);
	}
	public void getRelPointPos( float px, float py, float pz, Vector3 result){
		AndrODE.dBodyGetRelPointPos(handle, px, py, pz, result.data);
	}
	public void getRelPointVel( float px, float py, float pz, Vector3 result){
		AndrODE.dBodyGetRelPointVel(handle, px, py, pz, result.data);
	}
	public void getPointVel( float px, float py, float pz, Vector3 result){
		AndrODE.dBodyGetPointVel(handle, px, py, pz, result.data);
	}
	public void getPosRelPoint( float px, float py, float pz, Vector3 result){
		AndrODE.dBodyGetPosRelPoint(handle, px, py, pz, result.data);
	}
	public void vectorToWorld( float px, float py, float pz, Vector3 result){
		AndrODE.dBodyVectorToWorld(handle, px, py, pz, result.data);
	}
	public void vectorFromWorld( float px, float py, float pz, Vector3 result){
		AndrODE.dBodyVectorFromWorld(handle, px, py, pz, result.data);
	}
	public void setFiniteRotationMode ( int mode){
		AndrODE.dBodySetFiniteRotationMode (handle, mode);
	}
	public void setFiniteRotationAxis ( float x, float y, float z){
		AndrODE.dBodySetFiniteRotationAxis (handle, x, y, z);
	}
	public int getFiniteRotationMode (){
		return AndrODE.dBodyGetFiniteRotationMode (handle);
	}
	public void getFiniteRotationAxis ( Vector3 result){
		AndrODE.dBodyGetFiniteRotationAxis (handle, result.data);
	}
	public int getNumJoints (){
		return AndrODE.dBodyGetNumJoints (handle);
	}
	public Joint getJoint ( int index){
		return Joint.Construct(AndrODE.dBodyGetJoint (handle, index));
	}
	public void setDynamic (){
		AndrODE.dBodySetDynamic (handle);
	}
	public void setKinematic (){
		AndrODE.dBodySetKinematic (handle);
	}
	public int isKinematic (){
		return AndrODE.dBodyIsKinematic (handle);
	}
	public void setEnabled(boolean state){
		if (state){
			AndrODE.dBodyEnable (handle);
		}else{
			AndrODE.dBodyDisable (handle);
		}
	}
	public boolean isEnabled (){
		return (AndrODE.dBodyIsEnabled (handle)!=0);
	}
	public void setGravityMode (int mode){
		AndrODE.dBodySetGravityMode(handle,mode);
	}
	public int getGravityMode (){
		return AndrODE.dBodyGetGravityMode (handle);
	}
	public Geom getFirstGeom (){
		return new Geom(AndrODE.dBodyGetFirstGeom (handle));
	}
	public Geom getNextGeom (Geom geom){
		return new Geom(AndrODE.dBodyGetNextGeom (geom.handle));
	}
	public void setDampingDefaults(){
		AndrODE.dBodySetDampingDefaults(handle);
	}
	public float getLinearDamping (){
		return AndrODE.dBodyGetLinearDamping (handle);
	}
	public void setLinearDamping(float scale){
		AndrODE.dBodySetLinearDamping(handle, scale);
	}
	public float getAngularDamping (){
		return AndrODE.dBodyGetAngularDamping (handle);
	}
	public void setAngularDamping(float scale){
		AndrODE.dBodySetAngularDamping(handle, scale);
	}
	public void setDamping(float linear_scale, float angular_scale){
		AndrODE.dBodySetDamping(handle, linear_scale, angular_scale);
	}
	public float getLinearDampingThreshold (){
		return AndrODE.dBodyGetLinearDampingThreshold (handle);
	}
	public void setLinearDampingThreshold(float threshold){
		AndrODE.dBodySetLinearDampingThreshold(handle, threshold);
	}
	public float getAngularDampingThreshold (){
		return AndrODE.dBodyGetAngularDampingThreshold (handle);
	}
	public void setAngularDampingThreshold(float threshold){
		AndrODE.dBodySetAngularDampingThreshold(handle, threshold);
	}
	public float getMaxAngularSpeed (){
		return AndrODE.dBodyGetMaxAngularSpeed (handle);
	}
	public void setMaxAngularSpeed(float max_speed){
		AndrODE.dBodySetMaxAngularSpeed(handle, max_speed);
	}
	public int getGyroscopicMode(){
		return AndrODE.dBodyGetGyroscopicMode(handle);
	}
	public void setGyroscopicMode(int enabled){
		AndrODE.dBodySetGyroscopicMode(handle, enabled);
	}
	
	public boolean equals(Object b){
		if (b instanceof Body) {
			if (((Body)b).handle==handle){
				return true;
			}
		}
		return false;
	}
	
	private class bmlistener{
		BodyMoveListener cb;
		Body b;
		public bmlistener(Body B,BodyMoveListener CB){
			b=B;cb=CB;
		}
	}
	
	private static List<bmlistener> callbacks=new ArrayList<bmlistener>();

	public static void processCallbacks(){
		long[] handles=AndrODE.getCallbacksNative();
		for (long id:handles){
			Body b=new Body(id);
			
			for (bmlistener cb:callbacks){
				if (b.equals(cb.b)){
					cb.cb.bodyMoved(b);
				}
			}
		}
	}
	
	public void setMovedCallback(BodyMoveListener cb){
		callbacks.add(new bmlistener(this,cb));
		AndrODE.setCallbackNative(handle);
	}
}
